##
#   Project: BlueDUN
#            Internet connection manager for bluetooth devices.
#    Author: Fabio Castelli <muflone@vbsimple.net>
# Copyright: 2009 Fabio Castelli
#   License: GPL-2+
#  This program is free software; you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by the Free
#  Software Foundation; either version 2 of the License, or (at your option)
#  any later version.
# 
#  This program is distributed in the hope that it will be useful, but WITHOUT
#  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
#  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
#  more details.
# 
# On Debian GNU/Linux systems, the full text of the GNU General Public License
# can be found in the file /usr/share/common-licenses/GPL-2.
##

import gtk
import gtk.glade
import os
import stat
import grp
import rfcomms
import handlepaths
import gettext
from gettext import gettext as _

# Load domain for translation
for module in (gettext, gtk.glade):
  module.bindtextdomain(handlepaths.APP_NAME, handlepaths.getPath('locale'))
  module.textdomain(handlepaths.APP_NAME)

dialogConnection = None
controls = {}
FLD_WIDGET, FLD_DEFAULT, FLD_NEEDED, FLD_SAVE_DEFAULT = range(4)
getControl = lambda field: controls[field][FLD_WIDGET]

def on_optIPAddress_toggled(widget, data=None):
  "Set Force address field depending on the selected radio button"
  if getControl('IPAutomatic').get_active():
    getControl('Force Address').set_text('')
    getControl('Force Address').set_sensitive(False)
  else:
    getControl('Force Address').set_sensitive(True)

def on_optDNSServer_toggled(widget, data=None):
  "Set DNS Server fields depending on the selected radio button"
  if getControl('DNSAutomatic').get_active():
    getControl('DNS Server').set_text('')
    getControl('DNS Server').set_sensitive(False)
  else:
    getControl('DNS Server').set_sensitive(True)

def on_optDNSChecking_toggled(widget, data=None):
  "Set DNS Server fields depending on the selected radio button"
  if getControl('DNSCheckingNo').get_active():
    getControl('DNS Test 1').set_text('')
    getControl('DNS Test 2').set_text('')
    getControl('DNS Test 1').set_sensitive(False)
    getControl('DNS Test 2').set_sensitive(False)
  else:
    getControl('DNS Test 1').set_sensitive(True)
    getControl('DNS Test 2').set_sensitive(True)

def getWidgetString(widget):
  "Return the string value for the specified widget"
  if type(widget) is gtk.Entry:
    return widget.get_text()
  elif type(widget) is gtk.CheckButton:
    return widget.get_active() and 'on' or 'off'
  elif type(widget) is gtk.ComboBoxEntry:
    return widget.child.get_text()
  elif type(widget) is gtk.SpinButton:
    return widget.get_value_as_int()
  else:
    # print 'unknown type: %s' % str(type(widget))
    return None

def setWidgetString(widget, value):
  "Set value for the specified widget"
  if type(widget) is gtk.Entry:
    widget.set_text(value)
  elif type(widget) is gtk.CheckButton:
    widget.set_active(value.lower() in ('on', 'yes', 'ok', '1'))
  elif type(widget) is gtk.ComboBoxEntry:
    widget.child.set_text(value)
  elif type(widget) is gtk.SpinButton:
    widget.set_value(float(value))
  #else:
  #  print 'unknown type: %s' % str(type(widget))

def loadControls(gladeFile):
  "Load controls from the glade file"
  global dialogConnection
  global controls

  gw = gladeFile.get_widget
  # Main window
  dialogConnection = gw('dialogConnection')
  dialogConnection.set_icon_from_file(handlepaths.get_app_logo())
  dialogConnection.set_title(_('Edit connection'))
  # wr returns the widget itself, its default value and
  # a value that determines if saved even when default
  wr = lambda widget, needed, save: \
    (widget, getWidgetString(widget), needed, save)
  # General tab
  controls['Modem'] = wr(gw('cboModem'), True, True)
  controls['Baud'] = wr(gw('cboBaud'), True, True)
  controls['Username'] = wr(gw('txtLogin'), True, False)
  controls['Password'] = wr(gw('txtPassword'), True, False)
  controls['Remote Name'] = wr(gw('txtRemoteName'), False, False)
  controls['Idle Seconds'] = wr(gw('spinIdleSeconds'), True, False)
  controls['Auto Reconnect'] = wr(gw('chkAutoReconnect'), True, True)
  controls['Stupid Mode'] = wr(gw('chkStupidMode'), True, True)

  # Phone numbers tab
  controls['Dial Command'] = wr(gw('txtDialCommand'), True, True)
  controls['Dial Prefix'] = wr(gw('txtDialPrefix'), False, False)
  controls['Phone'] = wr(gw('txtPhone'), True, True)
  controls['Phone1'] = wr(gw('txtPhone1'), False, False)
  controls['Phone2'] = wr(gw('txtPhone2'), False, False)
  controls['Phone3'] = wr(gw('txtPhone3'), False, False)
  controls['Phone4'] = wr(gw('txtPhone4'), False, False)
  controls['Dial Attempts'] = wr(gw('spinDialAttempts'), True, False)

  # Addresses tab
  controls['Check Def Route'] = wr(gw('chkCheckDefaultRoute'), True, True)
  controls['IPAutomatic'] = wr(gw('optIPAddressAutomatic'), None, None)
  controls['IPManual'] = wr(gw('optIPAddressManual'), None, None)
  controls['Force Address'] = wr(gw('txtForceAddress'), False, False)
  controls['DNSAutomatic'] = wr(gw('optDNSServerAutomatic'), None, None)
  controls['DNSManual'] = wr(gw('optDNSServerManual'), None, None)
  controls['DNS Server'] = wr(gw('txtDNSServer'), False, False)
  controls['DNSCheckingNo'] = wr(gw('optDNSCheckingNo'), None, None)
  controls['DNSCheckingYes'] = wr(gw('optDNSCheckingYes'), None, None)
  controls['DNS Test 1'] = wr(gw('txtDNSTest1'), False, False)
  controls['DNS Test 2'] = wr(gw('txtDNSTest2'), False, False)
  
  # Initialization tab
  controls['Init1'] = wr(gw('txtInit1'), False, True)
  controls['Init2'] = wr(gw('txtInit2'), False, False)
  controls['Init3'] = wr(gw('txtInit3'), False, False)
  controls['Init4'] = wr(gw('txtInit4'), False, False)
  controls['Init5'] = wr(gw('txtInit5'), False, False)
  controls['Init6'] = wr(gw('txtInit6'), False, False)
  controls['Init7'] = wr(gw('txtInit7'), False, False)
  controls['Init8'] = wr(gw('txtInit8'), False, False)
  controls['Init9'] = wr(gw('txtInit9'), False, False)
  
  # Advanced tab
  controls['Login Prompt'] = wr(gw('txtLoginPrompt'), False, False)
  controls['Password Prompt'] = wr(gw('txtPasswordPrompt'), False, False)
  controls['Default Reply'] = wr(gw('txtDefaultReply'), False, False)
  controls['FlowControl'] = wr(gw('txtFlowControl'), False, False)
  controls['PPPD Path'] = wr(gw('txtPPPDPath'), False, False)
  controls['New PPPD'] = wr(gw('chkNewPPPD'), True, True)
  controls['Abort on No Dialtone'] = wr(gw('chkAbortNoDialtone'), True, True)
  controls['Abort on Busy'] = wr(gw('chkAbortBusy'), True, True)
  controls['Carrier Check'] = wr(gw('chkCarrierCheck'), True, True)

def loadFile(filename):
  "Load a saved configuration file"
  confFile = file(filename, 'r')
  for line in confFile:
    # Exclude lines beginning with # or [
    if line[0] not in '#[':
      # Split key and value
      key, value = map(str.strip, line[:-1].split('=', 2))
      # If it's a valid control name
      if controls.has_key(key):
        # Assign properties to widgets
        setWidgetString(controls[key][0], value)
      else:
        # Key not valid
        print 'no %s' % key
  confFile.close()
  # Set option buttons
  value = getWidgetString(getControl('Force Address'))
  getControl('IPAutomatic').set_active(len(value) == 0)
  getControl('IPManual').set_active(len(value) > 0)
  on_optIPAddress_toggled(None)

  value = getWidgetString(getControl('DNS Server'))
  getControl('DNSAutomatic').set_active(len(value) == 0)
  getControl('DNSManual').set_active(len(value) > 0)
  on_optDNSServer_toggled(None)

  value = getWidgetString(getControl('DNS Test 1'))
  if len(value) == 0:
    value = getWidgetString(getControl('DNS Test 2'))
  getControl('DNSCheckingNo').set_active(len(value) == 0)
  getControl('DNSCheckingYes').set_active(len(value) > 0)
  on_optDNSChecking_toggled(None)

def saveFile(filename):
  "Save the current connection information to a configuration file"
  confFile = file(filename, 'w')
  confFile.write('[Dialer BlueDUN]\n')
  for key, widget in controls.iteritems():
    value = getWidgetString(widget[FLD_WIDGET])
    # Save if not None and not default or if write default
    if value is not None and \
    (value != widget[FLD_DEFAULT] or widget[FLD_SAVE_DEFAULT]):
      confFile.write('%s = %s\n' % (key, value))
  confFile.close()
  
def loadWindow():
  "Load window and prepares its content"
  global dialogConnection

  gladeFile = gtk.glade.XML(
    fname=handlepaths.getPath('ui', 'connection.glade'),
    domain=handlepaths.APP_NAME)
  signals = {
    'on_optIPAddress_toggled': on_optIPAddress_toggled,
    'on_optDNSServer_toggled': on_optDNSServer_toggled,
    'on_optDNSChecking_toggled': on_optDNSChecking_toggled
  }
  gladeFile.signal_autoconnect(signals)
  loadControls(gladeFile)

  cell = gtk.CellRendererText()
  modelBaud = gtk.ListStore(str)
  bauds = (300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400,
    460800, 500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000,
    3000000, 3500000, 4000000)
  for baud in bauds:
    modelBaud.append([baud])
  cboBaud = controls['Baud'][0]
  cboBaud.pack_start(cell, True)
  cboBaud.set_model(modelBaud)
  cboBaud.set_text_column(0)
  cboBaud.child.set_text('460800')

  cell = gtk.CellRendererText()
  modelModem = gtk.ListStore(str)
  rfcomms.on_btnRefreshPorts_clicked(None)
  for port in rfcomms.modelPorts:
    modelModem.append(['/dev/%s' % port[1]])
  cboModem = controls['Modem'][0]
  cboModem.pack_start(cell, True)
  cboModem.set_model(modelModem)
  cboModem.set_text_column(0)
  
def showWindow(confFile):
  "Show the connection dialog"
  if dialogConnection.run() == gtk.RESPONSE_OK:
    # Check user's group to access to pppd
    dialogWarning = None
    pppd_path = getWidgetString(getControl('PPPD Path'))
    if len(pppd_path) == 0:
      pppd_path = '/usr/sbin/pppd'
    if os.path.exists(pppd_path):
      pppd_stat = os.stat(pppd_path)
#     if (pppd_stat.st_mode and stat.S_IXOTH) == 0 and \
#       pppd_stat.st_gid not in os.getgroups():
      if pppd_stat.st_gid not in os.getgroups():
        print 'WARNING: wrong permissions for %s (%d)\n' \
          'user not in "%s" (%d) group\n' \
          'current groups: %s' % (
          pppd_path, pppd_stat.st_mode, grp.getgrgid(pppd_stat.st_gid).gr_name,
          pppd_stat.st_gid, ', '.join(map(str, os.getgroups())))
        dialogWarning = gtk.MessageDialog(None, gtk.DIALOG_MODAL,
          gtk.MESSAGE_WARNING, gtk.BUTTONS_OK,
          _('WARNING: the current user is not in the "%s" group, which is '
          'required by %s to establish the PPP connection.\n\n'
          'Please add your user in the group to access to the PPPD, then '
          'disconnect your user, log in and try again.') % (
          grp.getgrgid(pppd_stat.st_gid).gr_name, pppd_path))
    else:
        print 'WARNING: pppd was not found on %s' % pppd_path
        dialogWarning = gtk.MessageDialog(None, gtk.DIALOG_MODAL,
          gtk.MESSAGE_WARNING, gtk.BUTTONS_OK,
          _('WARNING: the pppd daemon was not found on %s.\n' \
          'The connection could not work properly.\n\n' \
          'If pppd was moved or a specific pppd path was used please check it '
          'before attempting the connection.') % pppd_path)
    # Show warning dialog if it was created before
    if dialogWarning:
      dialogWarning.set_icon_from_file(handlepaths.get_app_logo())
      dialogWarning.run()
      dialogWarning.destroy()

    saveFile(confFile)
  dialogConnection.destroy()
